#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include "Thread.hpp"
#include "Log.hpp"
#include "Task.hpp"
using namespace std;
using namespace ThreadModule;
const static int gdefaultthreadnum = 3; // 默认线程数量
template <typename T>
class ThreadPool
{
private:
    // 锁住队列
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    // 解锁队列
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    // 线程去条件变量下去等待
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    // 唤醒一个线程
    void ThreadWaitUp()
    {
        pthread_cond_signal(&_cond);
    }
    // 唤醒所有线程
    void ThreadWaitUpAll()
    {
        pthread_cond_broadcast(&_cond);
    }

public:
    ThreadPool(int pthreadnum = gdefaultthreadnum)
        : _pthreadnum(pthreadnum), _waitnum(0), _isrunning(false)
    {
        // 初始化锁和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        LOG(Info, "线程池构造");
    }
    ~ThreadPool()
    {
        // 销毁锁和条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    // 创建出的线程执行的方法
    void HandlerTask(string name)
    {
        LOG(Info, "%s线程正在运行...", name.c_str());
        while (true)
        {
            // 首先保证任务队列是安全的
            // 加锁
            LockQueue();
            // 任务队列中无任务且线程池启动
            //  如果此时任务队列中没有任务且线程池启动运行，此线程就去条件变量下去等待
            while ((_task_queue.empty() == true) && (_isrunning == true))
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }
            // 任务队列中无任务且线程池退出，退出
            if ((_task_queue.empty() == true) && (_isrunning == false))
            {
                // 解锁
                UnlockQueue();
                // cout << name << "-线程退出......" << endl;
                break;
            }
            // 任务队列有任务，线程池退出---入任务，将所有任务都执行完
            // 任务队列有任务，线程池正常运行---入任务

            // 走到这里，此线程一定拿到了任务
            T t = _task_queue.front();
            _task_queue.pop();
            // 解锁
            UnlockQueue();
            LOG(DEBUG, "线程%s获取任务", name.c_str());
            // 处理任务
            t();
            LOG(DEBUG, "线程%s处理任务-结果为%s", name.c_str(), t.ResultToString().c_str());
        }
    }
    // 初始化线程池
    void InitThreadPool()
    {
        for (int num = 0; num < _pthreadnum; num++)
        {
            string name = "pthread-" + to_string(num + 1);
            //_threads.push_back(Thread(Print, name));
            Thread t(bind(&ThreadPool::HandlerTask, this, placeholders::_1), name);
            _threads.push_back(t);
            // bind作用:为了实现类的成员方法也可以成为另一个类的回调方法，方便我们进行类级别的互相调用
            LOG(Info, "创建线程%s成功", name.c_str());
        }
        _isrunning = true;
    }
    // 启动线程池
    void Start()
    {
        for (int i = 0; i < _pthreadnum; i++)
        {
            _threads[i].Start();
        }
        //_isrunning = true;
    }
    // 让线程池退出
    void Stop()
    {
        LockQueue();
        _isrunning = false;
        // 唤醒所有线程
        ThreadWaitUpAll();
        UnlockQueue();
    }
    // 等待所有线程
    void waitAll()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
            LOG(Info, "%s线程退出!", thread.name().c_str());
        }
    }
    // 添加任务到任务队列
    bool Enqueue(const T &t)
    {
        bool ret = false;
        // 添加任务时首先需要保护这个任务队列
        LockQueue();
        if (_isrunning == true)
        {
            // 任务入任务队列
            _task_queue.push(t);
            if (_waitnum > 0)
            {
                // 唤醒一个线程
                ThreadWaitUp();
            }
            LOG(DEBUG, "任务入队列成功");
            // 任务成功入任务队列之后是成功的
            ret = true;
        }
        UnlockQueue();
        return ret;
    }

private:
    int _pthreadnum;         // 线程数量
    vector<Thread> _threads; // 管理线程
    queue<T> _task_queue;    // 任务队列
    pthread_mutex_t _mutex;  // 锁---为保护临界资源(任务队列)
    pthread_cond_t _cond;    // 条件变量---为保护临界资源(任务队列)
    int _waitnum;            // 线程等待数
    bool _isrunning;         // 此线程池是否启动运行
};